#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Edge 收藏夹管理器 - GUI版本
使用Tkinter创建图形界面
"""

import json
import os
import shutil
import tkinter as tk
from tkinter import ttk, messagebox, filedialog, simpledialog
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Any
import threading


class EdgeBookmarksManager:
    def __init__(self):
        self.bookmarks_path = self._find_bookmarks_file()
        self.backup_dir = Path("bookmarks_backup")
        self.backup_dir.mkdir(exist_ok=True)
        
    def _find_bookmarks_file(self) -> Path:
        """查找Edge收藏夹文件路径"""
        edge_paths = [
            Path.home() / "AppData/Local/Microsoft/Edge/User Data/Default/Bookmarks",
            Path.home() / "AppData/Local/Microsoft/Edge/User Data/Profile 1/Bookmarks",
        ]
        
        for path in edge_paths:
            if path.exists():
                return path
                
        raise FileNotFoundError("未找到Edge收藏夹文件，请确保Edge已安装并至少运行过一次")
    
    def backup_bookmarks(self) -> str:
        """备份当前收藏夹"""
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        backup_file = self.backup_dir / f"bookmarks_backup_{timestamp}.json"
        shutil.copy2(self.bookmarks_path, backup_file)
        return str(backup_file)
    
    def load_bookmarks(self) -> Dict[str, Any]:
        """加载收藏夹数据"""
        try:
            with open(self.bookmarks_path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            raise Exception(f"读取收藏夹文件失败: {e}")
    
    def save_bookmarks(self, bookmarks_data: Dict[str, Any]) -> None:
        """保存收藏夹数据"""
        try:
            self.backup_bookmarks()
            with open(self.bookmarks_path, 'w', encoding='utf-8') as f:
                json.dump(bookmarks_data, f, ensure_ascii=False, indent=2)
        except Exception as e:
            raise Exception(f"保存收藏夹文件失败: {e}")
    
    def _find_folder_by_name(self, root: Dict, folder_name: str) -> Optional[Dict]:
        """根据名称查找文件夹"""
        if root.get('type') == 'folder' and root.get('name') == folder_name:
            return root
            
        if 'children' in root:
            for child in root['children']:
                result = self._find_folder_by_name(child, folder_name)
                if result:
                    return result
        return None
    
    def _search_bookmarks_recursive(self, node: Dict, keyword: str) -> List[Dict]:
        """递归搜索收藏夹"""
        results = []
        
        if node.get('type') == 'url':
            name = node.get('name', '').lower()
            url = node.get('url', '').lower()
            if keyword.lower() in name or keyword.lower() in url:
                results.append({
                    'name': node.get('name'),
                    'url': node.get('url'),
                    'date_added': node.get('date_added')
                })
        
        if 'children' in node:
            for child in node['children']:
                results.extend(self._search_bookmarks_recursive(child, keyword))
                
        return results    
    def add_bookmark(self, name: str, url: str, folder_name: str = "收藏夹栏") -> bool:
        """添加收藏夹"""
        try:
            bookmarks_data = self.load_bookmarks()
            
            bookmark_bar = bookmarks_data['roots']['bookmark_bar']
            target_folder = self._find_folder_by_name(bookmark_bar, folder_name)
            
            if not target_folder:
                new_folder = {
                    "date_added": str(int(datetime.now().timestamp() * 1000000)),
                    "date_modified": str(int(datetime.now().timestamp() * 1000000)),
                    "id": str(len(bookmarks_data.get('roots', {}).get('bookmark_bar', {}).get('children', [])) + 1),
                    "name": folder_name,
                    "type": "folder",
                    "children": []
                }
                bookmark_bar['children'].append(new_folder)
                target_folder = new_folder
            
            new_bookmark = {
                "date_added": str(int(datetime.now().timestamp() * 1000000)),
                "id": str(len(target_folder.get('children', [])) + 1),
                "name": name,
                "type": "url",
                "url": url
            }
            
            target_folder['children'].append(new_bookmark)
            self.save_bookmarks(bookmarks_data)
            return True
            
        except Exception as e:
            raise Exception(f"添加收藏夹失败: {e}")
    
    def search_bookmarks(self, keyword: str) -> List[Dict]:
        """搜索收藏夹"""
        try:
            bookmarks_data = self.load_bookmarks()
            results = []
            
            for root_name, root_data in bookmarks_data['roots'].items():
                if 'children' in root_data:
                    results.extend(self._search_bookmarks_recursive(root_data, keyword))
            
            return results
        except Exception as e:
            raise Exception(f"搜索收藏夹失败: {e}")
    
    def delete_bookmark(self, name: str) -> bool:
        """删除收藏夹"""
        try:
            bookmarks_data = self.load_bookmarks()
            deleted = False
            
            def delete_from_node(node):
                nonlocal deleted
                if 'children' in node:
                    original_children = node['children'][:]
                    node['children'] = [
                        child for child in node['children'] 
                        if not (child.get('type') == 'url' and child.get('name') == name)
                    ]
                    if len(node['children']) < len(original_children):
                        deleted = True
                    
                    for child in node['children']:
                        if child.get('type') == 'folder':
                            delete_from_node(child)
            
            for root_data in bookmarks_data['roots'].values():
                delete_from_node(root_data)
            
            if deleted:
                self.save_bookmarks(bookmarks_data)
                return True
            else:
                return False
                
        except Exception as e:
            raise Exception(f"删除收藏夹失败: {e}")
    
    def list_all_bookmarks(self) -> List[Dict]:
        """列出所有收藏夹"""
        try:
            bookmarks_data = self.load_bookmarks()
            all_bookmarks = []
            
            def extract_bookmarks(node, folder_path=""):
                if node.get('type') == 'url':
                    all_bookmarks.append({
                        'name': node.get('name'),
                        'url': node.get('url'),
                        'folder': folder_path,
                        'date_added': node.get('date_added')
                    })
                elif node.get('type') == 'folder':
                    current_path = f"{folder_path}/{node.get('name')}" if folder_path else node.get('name')
                    if 'children' in node:
                        for child in node['children']:
                            extract_bookmarks(child, current_path)
            
            for root_name, root_data in bookmarks_data['roots'].items():
                if 'children' in root_data:
                    for child in root_data['children']:
                        extract_bookmarks(child, root_name)
            
            return all_bookmarks
        except Exception as e:
            raise Exception(f"获取收藏夹列表失败: {e}")
    
    def export_bookmarks(self, output_file: str) -> bool:
        """导出收藏夹为HTML格式"""
        try:
            bookmarks = self.list_all_bookmarks()
            
            html_content = f"""<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Edge 收藏夹导出</title>
    <style>
        body {{ font-family: Arial, sans-serif; margin: 20px; }}
        .folder {{ margin: 10px 0; font-weight: bold; color: #0066cc; }}
        .bookmark {{ margin: 5px 0 5px 20px; }}
        .bookmark a {{ text-decoration: none; color: #0066cc; }}
        .bookmark a:hover {{ text-decoration: underline; }}
        .date {{ color: #666; font-size: 0.9em; }}
    </style>
</head>
<body>
    <h1>Edge 收藏夹导出</h1>
    <p>导出时间: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}</p>
"""
            
            current_folder = ""
            for bookmark in bookmarks:
                if bookmark['folder'] != current_folder:
                    if current_folder:
                        html_content += "</div>\n"
                    current_folder = bookmark['folder']
                    html_content += f'<div class="folder">{current_folder}</div>\n<div>\n'
                
                html_content += f'<div class="bookmark">'
                html_content += f'<a href="{bookmark["url"]}" target="_blank">{bookmark["name"]}</a>'
                if bookmark.get('date_added'):
                    try:
                        timestamp = int(bookmark['date_added']) / 1000000
                        date_str = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d")
                        html_content += f' <span class="date">({date_str})</span>'
                    except:
                        pass
                html_content += '</div>\n'
            
            if current_folder:
                html_content += "</div>\n"
            
            html_content += """
</body>
</html>"""
            
            with open(output_file, 'w', encoding='utf-8') as f:
                f.write(html_content)
            
            return True
        except Exception as e:
            raise Exception(f"导出收藏夹失败: {e}")


class BookmarksGUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Edge 收藏夹管理器")
        self.root.geometry("800x600")
        self.root.resizable(True, True)
        
        try:
            self.manager = EdgeBookmarksManager()
        except FileNotFoundError as e:
            messagebox.showerror("错误", str(e))
            self.root.destroy()
            return
        
        self.setup_ui()
        self.refresh_bookmarks_list()
    
    def setup_ui(self):
        """设置用户界面"""
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        
        # 配置网格权重
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        main_frame.rowconfigure(2, weight=1)
        
        # 标题
        title_label = ttk.Label(main_frame, text="Edge 收藏夹管理器", font=("Arial", 16, "bold"))
        title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20))
        
        # 操作按钮框架
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(0, 10))
        
        # 按钮
        ttk.Button(button_frame, text="添加收藏夹", command=self.add_bookmark_dialog).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(button_frame, text="删除选中", command=self.delete_selected_bookmark).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(button_frame, text="导出收藏夹", command=self.export_bookmarks_dialog).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(button_frame, text="备份收藏夹", command=self.backup_bookmarks).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(button_frame, text="刷新列表", command=self.refresh_bookmarks_list).pack(side=tk.LEFT, padx=(0, 5))
        
        # 搜索框架
        search_frame = ttk.Frame(main_frame)
        search_frame.grid(row=1, column=2, sticky=(tk.E), pady=(0, 10))
        
        ttk.Label(search_frame, text="搜索:").pack(side=tk.LEFT, padx=(10, 5))
        self.search_var = tk.StringVar()
        self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=20)
        self.search_entry.pack(side=tk.LEFT, padx=(0, 5))
        self.search_entry.bind('<KeyRelease>', self.on_search)
        ttk.Button(search_frame, text="搜索", command=self.search_bookmarks).pack(side=tk.LEFT)
        
        # 收藏夹列表框架
        list_frame = ttk.Frame(main_frame)
        list_frame.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S))
        list_frame.columnconfigure(0, weight=1)
        list_frame.rowconfigure(0, weight=1)
        
        # 创建Treeview
        columns = ('name', 'url', 'folder')
        self.tree = ttk.Treeview(list_frame, columns=columns, show='headings', height=15)
        
        # 定义列标题
        self.tree.heading('name', text='名称')
        self.tree.heading('url', text='网址')
        self.tree.heading('folder', text='文件夹')
        
        # 设置列宽
        self.tree.column('name', width=200, minwidth=150)
        self.tree.column('url', width=300, minwidth=200)
        self.tree.column('folder', width=150, minwidth=100)
        
        # 添加滚动条
        scrollbar_v = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.tree.yview)
        scrollbar_h = ttk.Scrollbar(list_frame, orient=tk.HORIZONTAL, command=self.tree.xview)
        self.tree.configure(yscrollcommand=scrollbar_v.set, xscrollcommand=scrollbar_h.set)
        
        # 布局
        self.tree.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        scrollbar_v.grid(row=0, column=1, sticky=(tk.N, tk.S))
        scrollbar_h.grid(row=1, column=0, sticky=(tk.W, tk.E))
        
        # 双击事件
        self.tree.bind('<Double-1>', self.on_double_click)
        
        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.grid(row=3, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=(10, 0)) 
   
    def refresh_bookmarks_list(self):
        """刷新收藏夹列表"""
        try:
            # 清空现有项目
            for item in self.tree.get_children():
                self.tree.delete(item)
            
            # 获取所有收藏夹
            bookmarks = self.manager.list_all_bookmarks()
            
            # 添加到树形视图
            for bookmark in bookmarks:
                self.tree.insert('', tk.END, values=(
                    bookmark['name'],
                    bookmark['url'],
                    bookmark['folder']
                ))
            
            self.status_var.set(f"已加载 {len(bookmarks)} 个收藏夹")
            
        except Exception as e:
            messagebox.showerror("错误", f"刷新收藏夹列表失败: {e}")
            self.status_var.set("刷新失败")
    
    def add_bookmark_dialog(self):
        """添加收藏夹对话框"""
        dialog = tk.Toplevel(self.root)
        dialog.title("添加收藏夹")
        dialog.geometry("400x200")
        dialog.resizable(False, False)
        dialog.transient(self.root)
        dialog.grab_set()
        
        # 居中显示
        dialog.geometry("+%d+%d" % (
            self.root.winfo_rootx() + 200,
            self.root.winfo_rooty() + 150
        ))
        
        frame = ttk.Frame(dialog, padding="20")
        frame.pack(fill=tk.BOTH, expand=True)
        
        # 名称
        ttk.Label(frame, text="名称:").grid(row=0, column=0, sticky=tk.W, pady=(0, 5))
        name_var = tk.StringVar()
        name_entry = ttk.Entry(frame, textvariable=name_var, width=40)
        name_entry.grid(row=0, column=1, pady=(0, 5), padx=(10, 0))
        name_entry.focus()
        
        # 网址
        ttk.Label(frame, text="网址:").grid(row=1, column=0, sticky=tk.W, pady=(0, 5))
        url_var = tk.StringVar()
        url_entry = ttk.Entry(frame, textvariable=url_var, width=40)
        url_entry.grid(row=1, column=1, pady=(0, 5), padx=(10, 0))
        
        # 文件夹
        ttk.Label(frame, text="文件夹:").grid(row=2, column=0, sticky=tk.W, pady=(0, 15))
        folder_var = tk.StringVar(value="收藏夹栏")
        folder_entry = ttk.Entry(frame, textvariable=folder_var, width=40)
        folder_entry.grid(row=2, column=1, pady=(0, 15), padx=(10, 0))
        
        # 按钮
        button_frame = ttk.Frame(frame)
        button_frame.grid(row=3, column=0, columnspan=2, pady=(10, 0))
        
        def add_bookmark():
            name = name_var.get().strip()
            url = url_var.get().strip()
            folder = folder_var.get().strip()
            
            if not name or not url:
                messagebox.showwarning("警告", "请填写名称和网址")
                return
            
            try:
                self.manager.add_bookmark(name, url, folder)
                messagebox.showinfo("成功", f"收藏夹 '{name}' 添加成功")
                dialog.destroy()
                self.refresh_bookmarks_list()
            except Exception as e:
                messagebox.showerror("错误", f"添加收藏夹失败: {e}")
        
        ttk.Button(button_frame, text="添加", command=add_bookmark).pack(side=tk.LEFT, padx=(0, 10))
        ttk.Button(button_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT)
        
        # 回车键添加
        dialog.bind('<Return>', lambda e: add_bookmark())
    
    def delete_selected_bookmark(self):
        """删除选中的收藏夹"""
        selection = self.tree.selection()
        if not selection:
            messagebox.showwarning("警告", "请先选择要删除的收藏夹")
            return
        
        item = selection[0]
        values = self.tree.item(item, 'values')
        bookmark_name = values[0]
        
        if messagebox.askyesno("确认删除", f"确定要删除收藏夹 '{bookmark_name}' 吗？"):
            try:
                if self.manager.delete_bookmark(bookmark_name):
                    messagebox.showinfo("成功", f"收藏夹 '{bookmark_name}' 删除成功")
                    self.refresh_bookmarks_list()
                else:
                    messagebox.showwarning("警告", f"未找到名为 '{bookmark_name}' 的收藏夹")
            except Exception as e:
                messagebox.showerror("错误", f"删除收藏夹失败: {e}")
    
    def search_bookmarks(self):
        """搜索收藏夹"""
        keyword = self.search_var.get().strip()
        if not keyword:
            self.refresh_bookmarks_list()
            return
        
        try:
            # 清空现有项目
            for item in self.tree.get_children():
                self.tree.delete(item)
            
            # 搜索收藏夹
            results = self.manager.search_bookmarks(keyword)
            
            # 添加搜索结果
            for bookmark in results:
                self.tree.insert('', tk.END, values=(
                    bookmark['name'],
                    bookmark['url'],
                    "搜索结果"
                ))
            
            self.status_var.set(f"找到 {len(results)} 个匹配结果")
            
        except Exception as e:
            messagebox.showerror("错误", f"搜索失败: {e}")
    
    def on_search(self, event):
        """搜索框内容变化时的处理"""
        # 延迟搜索，避免频繁搜索
        if hasattr(self, 'search_timer'):
            self.root.after_cancel(self.search_timer)
        self.search_timer = self.root.after(500, self.search_bookmarks)   
 
    def export_bookmarks_dialog(self):
        """导出收藏夹对话框"""
        filename = filedialog.asksaveasfilename(
            title="导出收藏夹",
            defaultextension=".html",
            filetypes=[("HTML文件", "*.html"), ("所有文件", "*.*")]
        )
        
        if filename:
            try:
                self.manager.export_bookmarks(filename)
                messagebox.showinfo("成功", f"收藏夹已导出到: {filename}")
                self.status_var.set(f"已导出到: {filename}")
            except Exception as e:
                messagebox.showerror("错误", f"导出失败: {e}")
    
    def backup_bookmarks(self):
        """备份收藏夹"""
        try:
            backup_file = self.manager.backup_bookmarks()
            messagebox.showinfo("成功", f"收藏夹已备份到: {backup_file}")
            self.status_var.set(f"已备份到: {backup_file}")
        except Exception as e:
            messagebox.showerror("错误", f"备份失败: {e}")
    
    def on_double_click(self, event):
        """双击事件处理"""
        selection = self.tree.selection()
        if selection:
            item = selection[0]
            values = self.tree.item(item, 'values')
            url = values[1]
            
            # 在默认浏览器中打开URL
            import webbrowser
            try:
                webbrowser.open(url)
                self.status_var.set(f"已打开: {url}")
            except Exception as e:
                messagebox.showerror("错误", f"无法打开网址: {e}")
    
    def run(self):
        """运行GUI"""
        try:
            self.root.mainloop()
        except Exception as e:
            messagebox.showerror("错误", f"程序运行出错: {e}")


def main():
    """主函数"""
    try:
        app = BookmarksGUI()
        app.run()
    except Exception as e:
        # 如果GUI初始化失败，显示错误消息
        root = tk.Tk()
        root.withdraw()  # 隐藏主窗口
        messagebox.showerror("错误", f"程序启动失败: {e}")


if __name__ == "__main__":
    main()